package com.cby.android.olive.utils

import android.text.TextUtils
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.File
import java.io.IOException
import java.util.*

object NetSecurityCheckUtils {

    private const val BUFFER_SIZE = 512

    /** xpose机制标识 */
    private const val XPOSED_BRIDGE = "de.robv.android.xposed.XposedBridge"

    /**
     * 检查root权限：user版本，继续查su文件//eng/userdebug版本，自带root权限
     */
    private val isRoot: Boolean
        get() {
            val secureProp = getRoSecureProp()
            return if (secureProp == 0) {
                //eng/userdebug版本，自带root权限
                true
            } else {
                //user版本，继续查su文件
                isSUExist
            }
        }

    private fun getRoSecureProp(): Int {
        val secureProp: Int
        val roSecureObj = getProperty("ro.secure")
        secureProp = if (roSecureObj == null) {
            1
        } else {
            if ("0" == roSecureObj) {
                0
            } else {
                1
            }
        }
        return secureProp
    }

    private val isSUExist: Boolean
        private get() {
            var file: File? = null
            val paths = arrayOf(
                "/sbin/su",
                "/system/bin/su",
                "/system/xbin/su",
                "/data/local/xbin/su",
                "/data/local/bin/su",
                "/system/sd/xbin/su",
                "/system/bin/failsafe/su",
                "/data/local/su"
            )
            for (path in paths) {
                file = File(path)
                if (file.exists()) {
                    return true
                }
            }
            return false
        }

    /**
     * 通过主动抛出异常，检查堆栈信息来判断是否存在XP框架
     */
    private val isXposedExistByThrow: Boolean
        get() = try {
            throw Exception("gg")
        } catch (e: Exception) {
            for (stackTraceElement in e.stackTrace) {
                if (stackTraceElement.className.contains(XPOSED_BRIDGE)) {
                    true
                }
            }
            false
        }

    /**
     * Android系统一个app一个uid
     * 如果同一uid下有两个进程对应的包名，在"/data/data"下有两个私有目录，则该应用被多开了
     */
    private fun checkByHasSameUid(): Boolean {
        val filter = uidStrFormat
        if (TextUtils.isEmpty(filter)) {
            return false
        }
        val result = exec("ps")
        if (TextUtils.isEmpty(result)) {
            return false
        }
        val lines = result!!.split("\n".toRegex()).toTypedArray()
        if (lines.isEmpty()) {
            return false
        }
        var exitDirCount = 0
        for (i in lines.indices) {
            if (lines[i].contains(filter!!)) {
                val pkgStartIndex = lines[i].lastIndexOf(" ")
                val processName = lines[i].substring(
                    if (pkgStartIndex <= 0) 0 else pkgStartIndex + 1,
                    lines[i].length
                )
                val dataFile = File(String.format("/data/data/%s", processName, Locale.CHINA))
                if (dataFile.exists()) {
                    exitDirCount++
                }
            }
        }
        return exitDirCount > 1
    }

    private val uidStrFormat: String?
        get() {
            var filter = exec("cat /proc/self/cgroup")
            if (filter == null || filter.isEmpty()) {
                return null
            }
            val uidStartIndex = filter.lastIndexOf("uid")
            var uidEndIndex = filter.lastIndexOf("/pid")
            if (uidStartIndex < 0) {
                return null
            }
            if (uidEndIndex <= 0) {
                uidEndIndex = filter.length
            }
            filter = filter.substring(uidStartIndex + 4, uidEndIndex)
            return try {
                val strUid = filter.replace("\n".toRegex(), "")
                if (isNumber(strUid)) {
                    val uid = Integer.valueOf(strUid)
                    filter = String.format("u0_a%d", uid - 10000)
                    return filter
                }
                null
            } catch (e: Exception) {
                e.printStackTrace()
                null
            }
        }

    private fun isNumber(str: String?): Boolean {
        if (str == null || str.isEmpty()) {
            return false
        }
        for (element in str) {
            if (!Character.isDigit(element)) {
                return false
            }
        }
        return true
    }

    /**
     * 检测手机环境是否安全
     */
    @JvmStatic
    val isSafePhoneEnvironment: Boolean
        get() = !isRoot && !isXposedExistByThrow && !checkByHasSameUid()

    /**
     * 获取属性
     *
     * @param propName String?
     * @return String?
     */
    fun getProperty(propName: String?): String? {
        var value: String? = null
        val roSecureObj: Any?
        try {
            roSecureObj = Class.forName("android.os.SystemProperties")
                .getMethod("get", String::class.java)
                .invoke(null, propName)
            if (roSecureObj != null) value = roSecureObj as String?
        } catch (e: Exception) {
            value = null
        } finally {
            return value
        }
    }


    /**
     * 执行命令
     * @param command String
     * @return String?
     */
    @JvmStatic
    fun exec(command: String): String? {
        var bufferedOutputStream: BufferedOutputStream? = null
        var bufferedInputStream: BufferedInputStream? = null
        var process: Process? = null
        return try {
            process = Runtime.getRuntime().exec("sh")
            bufferedOutputStream = BufferedOutputStream(process.outputStream)
            bufferedInputStream = BufferedInputStream(process.inputStream)
            bufferedOutputStream.write(command.toByteArray())
            bufferedOutputStream.write('\n'.toInt())
            bufferedOutputStream.flush()
            bufferedOutputStream.close()
            process.waitFor()
            getStrFromBufferInputSteam(bufferedInputStream)
        } catch (e: Exception) {
            null
        } finally {
            if (bufferedOutputStream != null) {
                try {
                    bufferedOutputStream.close()
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }
            if (bufferedInputStream != null) {
                try {
                    bufferedInputStream.close()
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }
            process?.destroy()
        }
    }

    /**
     * 将流转成字符串
     * @param bufferedInputStream BufferedInputStream?
     * @return String
     */
    private fun getStrFromBufferInputSteam(bufferedInputStream: BufferedInputStream?): String {
        if (null == bufferedInputStream) {
            return ""
        }

        val buffer = ByteArray(BUFFER_SIZE)
        val result = StringBuilder()
        try {
            while (true) {
                val read = bufferedInputStream.read(buffer)
                if (read > 0) {
                    result.append(String(buffer, 0, read))
                }
                if (read < BUFFER_SIZE) {
                    break
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return result.toString()
    }
}